﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using ClearingRestartingAutomaton.Base;
using ClearingRestartingAutomaton.Common;

namespace ClearingRestartingAutomaton
{
    public partial class ReduceGenerateForm : Form
    {
        public ReduceGenerateForm(Automaton automaton)
        {
            this.automaton = automaton;
            InitializeComponent();
        }

        private Automaton automaton;
        private string initialWord;
        private string leftContext;
        private string rightContext;
        private InstructionUse use;
        private int maxCount;
        private int maxLength;
        private WordTree wordTree;

        private bool ReadInitialConditions(bool showMessages)
        {
            if (RegexPatterns.WordRegex.IsMatch(initialWordTextBox.Text))
            {
                this.initialWord = initialWordTextBox.Text.Replace(".", "").Trim();
            }
            else
            {
                if (showMessages)
                {
                    MessageBox.Show("Initial word does not match the pattern.", "Warning");
                }
                return false;
            }
            if (RegexPatterns.LeftContextRegex.IsMatch(leftContextTextBox.Text))
            {
                this.leftContext = leftContextTextBox.Text.Replace(".", "").Trim();
            }
            else
            {
                if (showMessages)
                {
                    MessageBox.Show("Left context does not match the pattern.", "Warning");
                }
                return false;
            }
            if (RegexPatterns.RightContextRegex.IsMatch(rightContextTextBox.Text))
            {
                this.rightContext = rightContextTextBox.Text.Replace(".", "").Trim();
            }
            else
            {
                if (showMessages)
                {
                    MessageBox.Show("Right context does not match the pattern.", "Warning");
                }
                return false;
            }
            this.maxCount = (int)maxCountNumericUpDown.Value;
            this.maxLength = (int)maxLengthNumericUpDown.Value;
            return true;
        }

        private void Make()
        {
            if (this.ReadInitialConditions(true))
            {
                filterTextBox.Text = "^.*$";
                resultListView.Items.Clear();
                reduceFromListView.Items.Clear();
                reduceToListView.Items.Clear();
                this.wordTree = 
                    this.automaton.MakeWordTree(
                        this.initialWord, 
                        this.leftContext, 
                        this.rightContext, 
                        this.use, 
                        this.maxCount,
                        this.maxLength);
                foreach (string word in this.wordTree.TreeList)
                {
                    resultListView.Items.Add(word);
                }
            }
        }

        private void reduceButton_Click(object sender, EventArgs e)
        {
            this.use = InstructionUse.Reduction;
            this.Make();
        }

        private void generateButton_Click(object sender, EventArgs e)
        {
            this.use = InstructionUse.Generation;
            this.Make();
        }

        private void instructionsButton_Click(object sender, EventArgs e)
        {
            InformationForm infoForm = new InformationForm(this.automaton.ToString());
            infoForm.Show();
        }

        private void filterButton_Click(object sender, EventArgs e)
        {
            string filter_patt = this.filterTextBox.Text;
            try
            {
                Regex filter = new Regex(filter_patt);
                resultListView.Items.Clear();
                foreach (string word in this.wordTree.TreeList)
                {
                    if (filter.IsMatch(word))
                    {
                        resultListView.Items.Add(word);
                    }
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Invalid regex pattern.", "Warning");
            }
        }

        private void resultListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            stepRichTextBox.Clear();
            if (resultListView.SelectedItems.Count == 1)
            {
                string word = resultListView.SelectedItems[0].Text;
                InstructionStepCollection reduceFromStep = this.automaton.GenerateStep(word, this.leftContext, this.rightContext);
                InstructionStepCollection reduceToStep = this.automaton.ReduceStep(word, this.leftContext, this.rightContext);
                List<string> reduceFromList = new List<string>();
                List<string> reduceToList = new List<string>();
                foreach (InstructionStep instructionStep in reduceFromStep.Steps)
                {
                    if (!reduceFromList.Contains(instructionStep.ResultWord))
                    {
                        reduceFromList.Add(instructionStep.ResultWord);
                    }
                }
                foreach (InstructionStep instructionStep in reduceToStep.Steps)
                {
                    if (!reduceToList.Contains(instructionStep.ResultWord))
                    {
                        reduceToList.Add(instructionStep.ResultWord);
                    }
                }
                stepRichTextBox.Clear();
                reduceFromListView.Items.Clear();
                foreach (string resultWord in reduceFromList)
                {
                    ListViewItem item = reduceFromListView.Items.Add(resultWord);
                    if (this.wordTree.TreeSet.ContainsKey(resultWord))
                    {
                        item.ForeColor = Color.Black;
                    }
                    else
                    {
                        item.ForeColor = Color.Gray;
                    }
                }
                reduceToListView.Items.Clear();
                foreach (string resultWord in reduceToList)
                {
                    ListViewItem item = reduceToListView.Items.Add(resultWord);
                    if (this.wordTree.TreeSet.ContainsKey(resultWord))
                    {
                        item.ForeColor = Color.Black;
                    }
                    else
                    {
                        item.ForeColor = Color.Gray;
                    }
                }
            }
        }

        private void PrintReduction(string word, string resultWord)
        {
            System.Drawing.Font normalFont = stepRichTextBox.Font;
            System.Drawing.Font underlinedFont = new System.Drawing.Font(
                normalFont.FontFamily, normalFont.Size, System.Drawing.FontStyle.Underline);
            InstructionStepCollection reduceToStep = this.automaton.ReduceStep(word, this.leftContext, this.rightContext);
            foreach (InstructionStep instructionStep in reduceToStep.Steps)
            {
                if (instructionStep.ResultWord == resultWord)
                {
                    stepRichTextBox.Clear();
                    stepRichTextBox.SelectionLength = 0;
                    stepRichTextBox.SelectionFont = normalFont;
                    stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                    stepRichTextBox.AppendText(instructionStep.Instruction.ToString());
                    stepRichTextBox.AppendText(": ");
                    stepRichTextBox.AppendText(instructionStep.Left);
                    stepRichTextBox.SelectionLength = 0;
                    stepRichTextBox.SelectionFont = underlinedFont;
                    stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                    stepRichTextBox.AppendText(instructionStep.Center);
                    stepRichTextBox.SelectionLength = 0;
                    stepRichTextBox.SelectionFont = normalFont;
                    stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                    stepRichTextBox.AppendText(instructionStep.Right);
                    stepRichTextBox.AppendText(" -> ");
                    stepRichTextBox.AppendText(instructionStep.ResultWord);
                    if (instructionStep.CenterRewritten.Length > 0)
                    {
                        stepRichTextBox.SelectionStart =
                            stepRichTextBox.Text.Length -
                            instructionStep.ResultWord.Length +
                            instructionStep.StartIndex;
                        stepRichTextBox.SelectionLength = instructionStep.CenterRewritten.Length;
                        stepRichTextBox.SelectionColor = Color.Red;
                    }
                    stepRichTextBox.SelectionStart = stepRichTextBox.Text.Length;
                    stepRichTextBox.SelectionLength = 0;
                    stepRichTextBox.SelectionFont = normalFont;
                    stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                    break;
                }
            }
        }

        private void reduceFromListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (resultListView.SelectedItems.Count == 1 &&
                reduceFromListView.SelectedItems.Count == 1)
            {
                string word = reduceFromListView.SelectedItems[0].Text;
                string resultWord = resultListView.SelectedItems[0].Text;
                this.PrintReduction(word, resultWord);
            }
        }

        private void reduceToListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (resultListView.SelectedItems.Count == 1 &&
                reduceToListView.SelectedItems.Count == 1)
            {
                string word = resultListView.SelectedItems[0].Text;
                string resultWord = reduceToListView.SelectedItems[0].Text;
                this.PrintReduction(word, resultWord);
            }
        }

        private void resultListView_DoubleClick(object sender, EventArgs e)
        {
            System.Drawing.Font normalFont = stepRichTextBox.Font;
            System.Drawing.Font underlinedFont = new System.Drawing.Font(
                normalFont.FontFamily, normalFont.Size, System.Drawing.FontStyle.Underline);
            if (resultListView.SelectedItems.Count == 1)
            {
                string resultWord = resultListView.SelectedItems[0].Text;
                string separator = null;
                switch (this.wordTree.Use)
                {
                    case InstructionUse.Reduction:
                        separator = " => ";
                        break;
                    case InstructionUse.Generation:
                        separator = " -> ";
                        break;
                    default:
                        throw new InvalidProgramException("Unknown instruction use.");
                }
                int start = 0;
                int length = 0;
                InstructionStep instructionStep = new InstructionStep();
                stepRichTextBox.Clear();
                while (this.wordTree.TreeSet.ContainsKey(resultWord))
                {
                    if (this.wordTree.TreeSet[resultWord])
                    {
                        instructionStep = this.wordTree.TreeSteps[resultWord];
                        stepRichTextBox.SelectionLength = 0;
                        stepRichTextBox.SelectionFont = normalFont;
                        stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                        stepRichTextBox.AppendText(instructionStep.Left);
                        stepRichTextBox.SelectionLength = 0;
                        if (this.wordTree.Use == InstructionUse.Reduction)
                        {
                            stepRichTextBox.SelectionFont = normalFont;
                            stepRichTextBox.SelectionColor = System.Drawing.Color.Red;
                        }
                        else
                        {
                            stepRichTextBox.SelectionFont = underlinedFont;
                            stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                        }
                        stepRichTextBox.AppendText(instructionStep.CenterRewritten);
                        stepRichTextBox.SelectionLength = 0;
                        stepRichTextBox.SelectionFont = normalFont;
                        stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                        stepRichTextBox.AppendText(instructionStep.Right);
                    }
                    else
                    {
                        stepRichTextBox.SelectionLength = 0;
                        stepRichTextBox.SelectionFont = normalFont;
                        stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                        stepRichTextBox.AppendText(resultWord);
                    }
                    if (length > 0)
                    {
                        stepRichTextBox.SelectionStart =
                            stepRichTextBox.Text.Length - resultWord.Length + start;
                        stepRichTextBox.SelectionLength = length;
                        if (this.wordTree.Use == InstructionUse.Reduction)
                        {
                            stepRichTextBox.SelectionFont = underlinedFont;
                        }
                        else
                        {
                            stepRichTextBox.SelectionColor = Color.Red;
                        }
                        stepRichTextBox.SelectionStart = stepRichTextBox.Text.Length;
                        stepRichTextBox.SelectionLength = 0;
                        stepRichTextBox.SelectionFont = normalFont;
                        stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
                    }
                    if (this.wordTree.TreeSet[resultWord])
                    {
                        stepRichTextBox.AppendText(separator);
                    }
                    else
                    {
                        break;
                    }
                    start = instructionStep.StartIndex;
                    length = instructionStep.Length;
                    resultWord = instructionStep.Word;
                }
                stepRichTextBox.SelectionLength = 0;
                stepRichTextBox.SelectionFont = normalFont;
                stepRichTextBox.SelectionColor = System.Drawing.Color.Black;
            }
        }

        private void SelectItemInResultListView(ListView listView)
        {
            if (listView.SelectedItems.Count == 1)
            {
                string word = listView.SelectedItems[0].Text;
                ListViewItem item = resultListView.FindItemWithText(word, false, 0, false);
                if (item != null)
                {
                    resultListView.Items[item.Index].Selected = true;
                }
                else
                {
                    ListViewItem newItem = resultListView.Items.Add(word);
                    if (!this.wordTree.TreeSet.ContainsKey(word))
                    {
                        newItem.ForeColor = Color.Gray;
                    }
                    newItem.Selected = true;
                }
            }
        }

        private void reduceFromListView_DoubleClick(object sender, EventArgs e)
        {
            this.SelectItemInResultListView(reduceFromListView);
        }

        private void reduceToListView_DoubleClick(object sender, EventArgs e)
        {
            this.SelectItemInResultListView(reduceToListView);
        }
    }
}
